package com.cuiweiyou.vigilesmicrecord.util;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Handler;
import android.os.Message;

import com.cuiweiyou.vigilesmicrecord.base.ContantValue;
import com.cuiweiyou.vigilesmicrecord.util.comm.DateFormatUtil;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

public class RecordUtil {

    private final int audioSource = MediaRecorder.AudioSource.MIC;    // 声源
    private final int audioFormat = AudioFormat.ENCODING_PCM_16BIT;   // 采样精度，一个采样点16比特，相当于2个字节。
    private final int channelConfig = AudioFormat.CHANNEL_IN_STEREO;  // CHANNEL_IN_STEREO双声道，CHANNEL_IN_MONO单声道
    private final int sampleRateInHz = 16000;                         // 采样率。
    private final int bufferSize = AudioRecord.getMinBufferSize(
            sampleRateInHz,
            channelConfig,
            audioFormat);

    private static RecordUtil instance;
    private AudioRecord audioRecord;

    private RecordThread recordThread;

    private OnRecordStopListener onRecordStopListener;
    private boolean isRecording = false;

    private Handler volumeHandler;
    private Handler onRecordFinishHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            isRecording = false;

            String wavFilePath = (String) msg.obj;
            if (null != onRecordStopListener) {
                onRecordStopListener.recordStop(wavFilePath);
            }
        }
    };

    private RecordUtil() {
        initAudioRecord();
    }

    private void initAudioRecord() {
        audioRecord = new AudioRecord(
                audioSource,
                sampleRateInHz,
                channelConfig,
                audioFormat,
                bufferSize * 2);
    }

    public static RecordUtil getInstance() {
        if (instance == null) {
            synchronized (RecordUtil.class) {
                if (instance == null) {
                    instance = new RecordUtil();
                }
            }
        }
        return instance;
    }

    public void init() {
        getInstance();
    }

    public void setOnRecordStopListener(OnRecordStopListener listener) {
        onRecordStopListener = listener;
    }

    public void setSoundHandler(Handler handler) {
        this.volumeHandler = handler;
    }

    public boolean isRecording() {
        return isRecording;
    }

    public void startRecord() {
        isRecording = true;

        String yyyyMMddHHmmss = DateFormatUtil.millis2yyyyMMddHHmmss(new Date().getTime());
        String soundSavePath = ContantValue.getSoundSavePath();
        String filePath = soundSavePath + yyyyMMddHHmmss;

        recordThread = new RecordThread(filePath);
        recordThread.start();
    }

    public void stopRecord() {
        if (null != recordThread) {
            recordThread.setDestroy(true);
        }
        recordThread = null;
    }

    public void release() {
        stopRecord();
        audioRecord.release();
    }

    class RecordThread extends Thread {
        private boolean isDestroy = false;
        private File tmpFile;
        private File wavFile;

        public RecordThread(String filePath) {
            tmpFile = new File(filePath + ".pcm");
            wavFile = new File(filePath + ".wav");
        }

        public void setDestroy(boolean isDestroy) {
            this.isDestroy = isDestroy;
        }

        @Override
        public void run() {
            if (null != volumeHandler) {
                Message msg = volumeHandler.obtainMessage();
                msg.obj = -1d;
                volumeHandler.sendMessage(msg);
            }

            if (AudioRecord.STATE_INITIALIZED != audioRecord.getState()) {
                initAudioRecord();
            }
            audioRecord.startRecording();

            PcmBufferRunnable bufferRunnable = new PcmBufferRunnable(tmpFile, onPcmBufferFinishListener);
            new Thread(bufferRunnable).start();

            while (!isDestroy) {
                byte[] buffer = new byte[bufferSize];
                int length = audioRecord.read(buffer, 0, bufferSize);

                bufferRunnable.add(buffer);

                long v = 0;
                for (int i = 0; i < buffer.length; i++) {
                    v += buffer[i] * buffer[i];
                }
                double mean = v / (double) length;
                double volume = 10 * Math.log10(mean);

                if (null != volumeHandler) {
                    Message msg = volumeHandler.obtainMessage();
                    msg.obj = volume;
                    volumeHandler.sendMessage(msg);
                }

            }//--------- while

            audioRecord.stop();

            bufferRunnable.disableWriting();
        }

        OnPcmBufferFinishListener onPcmBufferFinishListener = new OnPcmBufferFinishListener() {
            @Override
            public void onPcmBufferFinish() {

                PCM2WAVUtil.pcm2wav(sampleRateInHz, bufferSize, tmpFile.getAbsolutePath(), wavFile.getAbsolutePath());

                tmpFile.delete();

                if (null != onRecordFinishHandler) {
                    Message msgw = onRecordFinishHandler.obtainMessage();
                    msgw.obj = wavFile.getAbsolutePath();
                    onRecordFinishHandler.sendMessage(msgw);
                }
            }
        };
    }

    public interface OnRecordStopListener {
        void recordStop(String wavFilePath);
    }

    class PcmBufferRunnable implements Runnable {
        private List<byte[]> bufferSession = Collections.synchronizedList(new ArrayList<byte[]>()); // 第二级
        private List<byte[]> bufferCache = Collections.synchronizedList(new ArrayList<byte[]>());   // 第一级
        private OnPcmBufferFinishListener onPcmBufferFinishListener;
        private boolean writingAbble = true;
        private BufferedOutputStream bos;

        public PcmBufferRunnable(File tmpPcmFile, OnPcmBufferFinishListener listener) {
            onPcmBufferFinishListener = listener;

            try {
                tmpPcmFile.createNewFile();
                FileOutputStream out = new FileOutputStream(tmpPcmFile.getAbsolutePath());
                bos = new BufferedOutputStream(out);
            } catch (Exception e) {
            }
        }

        public void disableWriting() {
            writingAbble = false;
        }

        public void add(byte[] bts) {
            bufferSession.add(bts);
            if (bufferCache.size() < 1) {
                bufferCache.addAll(bufferSession);
                bufferSession.clear();
            }
        }

        @Override
        public void run() {
            int size;
            while (writingAbble) {
                size = bufferCache.size();
                if (null != bos && size > 0) {
                    try {
                        byte[] bts = bufferCache.remove(0);
                        bos.write(bts);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }

            //////////////////////////////////////// 读取第二级缓存

            if (bufferSession.size() > 0) {
                bufferCache.addAll(bufferSession);
                bufferSession.clear();
            }

            size = bufferCache.size();
            if (size > 0) {
                for (int i = 0; i < size; i++) {
                    byte[] bts = bufferCache.get(i);
                    if (null != bos) {
                        try {
                            bos.write(bts);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

            if (bos != null) {
                try {
                    bos.flush();
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            bos = null;
            bufferCache.clear();

            if (null != onPcmBufferFinishListener) {
                onPcmBufferFinishListener.onPcmBufferFinish();
            }
        }
    }

    public interface OnPcmBufferFinishListener {
        void onPcmBufferFinish();
    }
}


